/**
 * 
 */
package controllers;

import java.util.*;

import com.fasterxml.jackson.databind.node.ObjectNode;

import models.Table;
import play.*;
import play.libs.Json;
import play.mvc.*;
import views.html.*;
import models.Table;
import models.Client;

/**
 * @author Administrator
 *
 */
@SuppressWarnings("unused")
public class Game {
	
	final static String HUMAN = "Human";
	final static String BOT = "Bot";
	
	public static boolean Ready =  false;
	public static boolean Started = false;
	
	// wymagana liczba graczy
	public static int HumansNumber = 2;
	public static int BotsNumber = 2;
	public static int TotalParticipantsNumber = 4;
	public static int StartToken = 300;
	public static int Ante = 10;
				
	// lista graczy ktorzy weszli do gry - stolu
	public static Client Players[] = null;
	
	// lista partycypantow lolole
	public static String Participants[][] = null;
	
	// referencja do stolu
	private static Table GameTable = null;
	
	public static void GameCreate( Client C ) throws Exception {
		
		// create new array of players
		Players = new Client[HumansNumber];
		Participants = new String[4][2];
		
		for ( int i=0; i<HumansNumber; i++ )
			Players[i] = null;
		
		// add the host to the array
		for ( int i=0; i<HumansNumber; i++ ) {
			if ( Players[i] != null )
				if ( Players[i].equals(C) )	
					break;

    		if ( Players[i] == null ) {	
    			Players[i] = C;
    			break;
    		}
    	}
		
		Ready = true;
		
		Clients.SendMessageToAll("game_status","game_created");
		
		if ( AllReady() ) {
			GameStart();
		}
		
		// wait for other players ...
	}
	
	public static void GameStart() {
		Started = true;
		Ready = true;
		
		RemoveLobbyPlayers();
		
    	Clients.SendMessageToAll("game_status","game_started");
    	
    	if ( HumansNumber+BotsNumber != TotalParticipantsNumber ) return; // woops

    	for (int i=0; i<TotalParticipantsNumber; i++)
    	{
    		Participants[i][0] = ""; // name
    		Participants[i][1] = ""; // bot or human ?
    	}
    	
    	for (int i=0; i<HumansNumber; i++) {
    		Participants[i][0] = Players[i].getUser();
    		Participants[i][1] = HUMAN;
    	}
    	
    	for (int i=HumansNumber; i<TotalParticipantsNumber; i++) {
    		Participants[i][0] = "Bot "+Integer.toString(i);
    		Participants[i][1] = BOT;
    	}
    	
    	for (int i=0; i<TotalParticipantsNumber; i++)
    	{
    		Clients.SendMessageToAll("send_player", Participants[i][0]);
    	}
    	
		
		// call the Poker table to start 
		// ...
    	if ( TotalParticipantsNumber == 2 ) {
    		GameTable = new Table(Participants[0][0],Participants[1][0],GetType(Participants[0][1]),GetType(Participants[1][1]),StartToken,Ante);
    	}
    	else
    	if ( TotalParticipantsNumber == 3 ) {
    		GameTable = new Table(Participants[0][0],Participants[1][0],Participants[2][0],GetType(Participants[0][1]),GetType(Participants[1][1]),GetType(Participants[2][1]),StartToken,Ante);
        }
    	else
        if ( TotalParticipantsNumber == 4 ) {
        	GameTable = new Table(Participants[0][0],Participants[1][0],Participants[2][0],Participants[3][0],GetType(Participants[0][1]),GetType(Participants[1][1]),GetType(Participants[2][1]),GetType(Participants[3][1]),StartToken,Ante);
        }
		
    	GameTable.setGameController(Application.GameController);
		GameTable.start();
	}
	
	public static void GameStop() {
		Started = false;
		Ready = false;
		
		//for ( int i=0; i<HumansNumber; i++ )
			//Players[i] = null;
		
		Clients.SendMessageToAll("game_status","game_stopped");
		// quit the current Poker table
	}
	
	public static void GameJoin( Client C ) throws Exception {
		
		if ( Game.IsRunning() ) {
			throw new Exception("The game is in progress!");
		}
		
		// add new player to the array
		for ( int i=0; i<HumansNumber; i++ ) {
			if ( Players[i] != null )
				if ( Players[i].equals(C) ) 
					throw new Exception("Already in game!");
			
			if ( Players[i] == null ) {
				Players[i] = C;
				
				Clients.SendMessageToAll("game_joined",C.getUser());
				
				if ( AllReady() ) {
					GameStart();
				}
					
				return;
			}
		}
		
		// else throw exception
		throw new Exception("Game is full!");
	}
	
	public static void GameLeft( Client C ) throws Exception {
		
		if ( !IsReady() ) return;
		
		int PlayerID = GetPlayerID(C);
		
		for ( int i=0; i<HumansNumber; i++ ) {
			if ( Players[i] != null )
				if ( Players[i].equals(C) ) 
					Players[i] = null;
		}
		if ( PlayerID >= 0 ) GameTable.playerLeave(PlayerID);
		Clients.SendMessageToAll("game_left",C.getUser());
		
	}
	
	public static void ReplaceCards( Client C, int[] Cards ) throws Exception {
		
		GameTable.move(GetPlayerID(C),Cards);
		GameTable.endTurn(GetPlayerID(C));
	}
	
	public static void Confirm( Client C ) throws Exception {
		
		GameTable.move(GetPlayerID(C));
		GameTable.endTurn(GetPlayerID(C));
	}

	public static void Bet( Client C, int Val ) throws Exception {
		
		GameTable.move(GetPlayerID(C),GameTable.MO_BET, Val);
		GameTable.endTurn(GetPlayerID(C));
	}

	public static void Raise( Client C, int Val ) throws Exception {
	
		GameTable.move(GetPlayerID(C),GameTable.MO_RAISE, Val);
		GameTable.endTurn(GetPlayerID(C));
	}

	public static void Check( Client C ) throws Exception {
	
		GameTable.move(GetPlayerID(C),GameTable.MO_CHECK, 0);
		GameTable.endTurn(GetPlayerID(C));
	}
	
	public static void Allin( Client C ) throws Exception {
		
		GameTable.move(GetPlayerID(C),GameTable.MO_ALLIN, 0);
		GameTable.endTurn(GetPlayerID(C));
	}
	
	public static void Call( Client C ) throws Exception {
		
		GameTable.move(GetPlayerID(C),GameTable.MO_CALL, 0);
		GameTable.endTurn(GetPlayerID(C));
	}
	
	public static void Fold( Client C ) throws Exception {
		
		GameTable.move(GetPlayerID(C),GameTable.MO_FOLD, 0);
		GameTable.endTurn(GetPlayerID(C));
	}
	
	public static boolean AllReady() {
		
		// check if players array is full
		for ( int i=0; i<HumansNumber; i++ ) {
    		if ( Players[i] == null ) {	
    			return false;
    		}
    	}
		
		return true;
	}
	
	public static boolean CheckSettings() throws Exception {	
		
		Game.TotalParticipantsNumber = Game.HumansNumber + Game.BotsNumber;
		
		if ( Game.HumansNumber < 1 ) {
			throw new Exception("Must be at least one human player!");
		}
		
		if ( Game.TotalParticipantsNumber > 4 || Game.TotalParticipantsNumber < 2 ) {
			throw new Exception("Wrong number of players!");
		}
		
		if ( Game.Ante <= 0 || Game.StartToken <= 0 ) {
			throw new Exception("Ante or starting tokens are too low!");
		}
		
		if ( Game.Ante >= Game.StartToken ) {
			throw new Exception("Ante is too high!");
		}
		
		return true;
	}
	
	public static int GetPlayerID(Client C) {
		for ( int i=0; i<HumansNumber; i++ ) {
			if ( Players[i] != null )
				if ( Players[i].equals(C) ) 
					return i;
		}
		
		return -1;
	}
	
	public static boolean IsReady() {
		return Ready;
	}
	
	public static boolean IsRunning() {
		return Started;
	}
	
	public static void RemoveLobbyPlayers() {
		
		for (int i=0; i<Clients.GetClientsNumber(); i++) {
			boolean found = false;
			
			for (int j=0; j<HumansNumber; j++) {
				if ( Players[j].equals(Clients.ClientsArray[i]) ) {
					found = true;
					break;
				}
			}
			
			if ( !found ) {
				Clients.SendMessageTo(Clients.ClientsArray[i],"remove",Clients.ClientsArray[i].getUser());
			}
		}
	}
	
	public void NotifyWrongMove( int PlayerID, String error ) {
		
		Clients.SendMessageTo(Clients.GetClientByName(GameTable.getPlayerByID(PlayerID).getName()),"game_error",error);
	}
	
	public void NotifyStartGame( final byte Status ) {
		
		for (int j=0; j<TotalParticipantsNumber; j++)
		{
			for (int i=0; i<GameTable.getPlayersCardsID(j).length; i++) {
			
				ObjectNode event = Json.newObject();
				event.put("send_card",GameTable.getPlayerByID(j).getName());
				event.put("card_id",GameTable.getPlayersCardsID(j)[i]);
				
				//System.out.println(event);
				Clients.SendMessageTo(Clients.GetClientByID(j),event);
			}
			
			ObjectNode event = Json.newObject();
			event.put("send_info",GameTable.getPlayerByID(j).getName());
			event.put("last_action",GameTable.MO_READY);
			event.put("current_bet",GameTable.getLastBet()[j]);
			event.put("current_pot",GameTable.getBets()[j]);
			event.put("current_tokens",GameTable.getPlayerByID(j).getTokens());
			
			//System.out.println(event);
			
			Clients.SendMessageToAll(event);
		}
	}
	
	public void NotifyEndGame( final byte Status, String Summary ) {
		
			System.out.println("EMDGAME");
		
			Clients.SendMessageToAll("game_status","game_end");
			Clients.SendMessageToAll("game_summary",Summary);
			
			for (int j=0; j<HumansNumber; j++)
			{
				if ( Players[j] != null ) 
					Clients.ClientDisconnect(Players[j].getID());
			}
			
			if ( Clients.GetClientsNumber() == 0 ) GameTable = null;
	}
	
	public void NotifyNewTurn( int PlayerID, final byte Ability, final byte Status ) {
		
		System.out.println("NEWTURN - notify");
		
		Clients.SendMessageToAll("new_turn",GameTable.getPlayerByID(PlayerID).getName());
		Clients.SendMessageToAll("turn_status",Status);
		
		//System.out.println("new_turn:"+GameTable.getPlayerByID(PlayerID).getName());
		
		for (int j=0; j<TotalParticipantsNumber; j++)
		{
			for (int i=0; i<GameTable.getPlayersCardsID(j).length; i++) {
			
				ObjectNode event = Json.newObject();
				event.put("send_card",GameTable.getPlayerByID(j).getName());
				event.put("card_id",GameTable.getPlayersCardsID(j)[i]);

				if ( Status != GameTable.GS_SUMUP )
					Clients.SendMessageTo(Clients.GetClientByID(j),event);
				else
					Clients.SendMessageToAll(event);
			}
			
			//System.out.println(event);
			ObjectNode event = Json.newObject();
			event.put("send_info",GameTable.getPlayerByID(j).getName());
			event.put("last_action",GameTable.getLastMovement()[j]);
			event.put("current_bet",GameTable.getLastBet()[j]);
			event.put("current_pot",GameTable.getBets()[j]);
			event.put("current_tokens",GameTable.getPlayerByID(j).getTokens());
			
			
			Clients.SendMessageToAll(event);
		}
		
		if ( Status == GameTable.GS_SUMUP )
			Clients.SendMessageTo(Clients.GetClientByName(GameTable.getPlayerByID(PlayerID).getName()),"send_sumup",GameTable.getSumUpString());
		
		//System.out.println(Integer.toString(PlayerID)+" - "+Integer.toString(Ability));
		//System.out.println(Clients.GetClientByID(PlayerID).getUser());
		Clients.SendMessageTo(Clients.GetClientByName(GameTable.getPlayerByID(PlayerID).getName()),"turn_ability",Ability);
	}
	
	public void NotifyEndTurn( int PlayerID, final byte Action, final byte Status ) {
		
		//System.out.println(Integer.toString(PlayerID)+" - end turn!");
		
		Clients.SendMessageToAll("end_turn",GameTable.getPlayerByID(PlayerID).getName());
		Clients.SendMessageToAll("turn_status",Status);
		
		for (int j=0; j<TotalParticipantsNumber; j++)
		{
			for (int i=0; i<GameTable.getPlayersCardsID(j).length; i++) {
			
				ObjectNode event = Json.newObject();
				event.put("send_card",GameTable.getPlayerByID(j).getName());
				event.put("card_id",GameTable.getPlayersCardsID(j)[i]);
				
				//System.out.println(event);
				Clients.SendMessageTo(Clients.GetClientByID(j),event);
			}
			
			
		}
		
		ObjectNode event = Json.newObject();
		event.put("send_action",GameTable.getPlayerByID(PlayerID).getName());
		event.put("action",Action);
		event.put("value",GameTable.getLastBet()[PlayerID]);
		
		//System.out.println(event);
		Clients.SendMessageToAll(event);
	}
	
	private static boolean GetType(String s) {
		if ( s.equals(HUMAN)) return false;
		if ( s.equals(BOT)) return true;
		
		return false;
	}

	public Table getGameTable() {
		return GameTable;
	}
}
